﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Protell.Model;
using Google.GData.Spreadsheets;
using Google.GData.Client;
using Protell.Model.IRepository;
using System.Collections.ObjectModel;

namespace Protell.ViewModel
{
    public class Send2GDViewModel : ViewModelBase
    {
        //Repositories
        private ISettingRepository _SettingRepository;
        private IGdDatosEstructurasRepository _GDDatEstRepository;
        private int INTERVAL_NUM = 3000;

        //Declaracion datos privados
        private SpreadsheetsService _GDService;
        private CellQuery _CellQuery;
        private CellFeed _CellFeed;
        public class CellAddress
        {
            public uint Row;
            public uint Col;
            public string Value;
            public string IdString;

            /**
             * Constructs a CellAddress representing the specified {@code row} and
             * {@code col}. The IdString will be set in 'RnCn' notation.
             */
            public CellAddress(uint row, uint col, string value)
            {
                this.Row = row;
                this.Col = col;
                this.Value = value;
                this.IdString = string.Format("R{0}C{1}", row, col);
            }
        }
        private List<CellAddress> cellAddrs;

        //Properties
        public SettingModel Settings
        {
            get { return _Settings; }
            set
            {
                if (_Settings != value)
                {
                    _Settings = value;
                    OnPropertyChanged(SettingsPropertyName);
                }
            }
        }
        private SettingModel _Settings;
        public const string SettingsPropertyName = "Settings";

        public ObservableCollection<GdDatosEstructurasModel> DatosEsts
        {
            get { return _DatosEsts; }
            set
            {
                if (_DatosEsts != value)
                {
                    _DatosEsts = value;
                    OnPropertyChanged(DatosEstsPropertyName);
                }
            }
        }
        private ObservableCollection<GdDatosEstructurasModel> _DatosEsts;
        public const string DatosEstsPropertyName = "DatosEsts";

        //Methods
        /// <summary>
        /// Cargar la lista de CellAddress
        /// </summary>
        public void LoadCellAddress()
        {
            try
            {
                this.cellAddrs = (from o in this.DatosEsts
                                  select new CellAddress((uint)o.NumeroFila, (uint)o.NumeroColumna, o.Valor == null ? "" : o.Valor.ToString())
                              ).ToList<CellAddress>();

                (from o in this.DatosEsts
                 where o.NombreColumna != null
                 select new { o.NombreColumna, NumeroColumna = 2, o.NumeroFila }).Distinct().ToList().ForEach(p =>
                 {
                     this.cellAddrs.Add(new CellAddress((uint)p.NumeroFila, (uint)p.NumeroColumna, p.NombreColumna));
                 });


            }
            catch (Exception)
            {

                this.cellAddrs = null;
            }
        }

        public List<CellAddress> LoadCellAddress(int to, int from)
        {
            List<CellAddress> cells = new List<CellAddress>();
            for (int i = to; i < from; i++)
            {
                if (i < this.DatosEsts.Count)
                {
                    cells.Add(new CellAddress((uint)this.DatosEsts[i].NumeroFila, (uint)this.DatosEsts[i].NumeroColumna, this.DatosEsts[i].Valor == null ? "" : this.DatosEsts[i].Valor.ToString()));
                }

            }
            return cells;
        }

        public List<CellAddress> LoadLabelAddress()
        {
            List<CellAddress> cells = new List<CellAddress>();

            (from o in this.DatosEsts
             where o.NombreColumna != null
             select new { o.NombreColumna, NumeroColumna = 2, o.NumeroFila }).Distinct().ToList().ForEach(p =>
             {
                 cells.Add(new CellAddress((uint)p.NumeroFila, (uint)p.NumeroColumna, p.NombreColumna));
             });

            return cells;
        }

        public void UpadateData()
        {
            Dictionary<String, CellEntry> cellEntries = GetCellEntryMap(this._GDService, this._CellFeed, this.cellAddrs);

            CellFeed batchRequest = new CellFeed(this._CellQuery.Uri, this._GDService);
            foreach (CellAddress cellAddr in cellAddrs)
            {
                CellEntry batchEntry = cellEntries[cellAddr.IdString];
                batchEntry.InputValue = cellAddr.Value.ToString();
                batchEntry.BatchData = new GDataBatchEntryData(cellAddr.IdString, GDataBatchOperationType.update);
                batchRequest.Entries.Add(batchEntry);
            }

            // Submit the update
            CellFeed batchResponse = (CellFeed)this._GDService.Batch(batchRequest, new Uri(this._CellFeed.Batch));

            // Check the results
            bool isSuccess = true;
            foreach (CellEntry entry in batchResponse.Entries)
            {
                string batchId = entry.BatchData.Id;
                if (entry.BatchData.Status.Code != 200)
                {
                    isSuccess = false;
                    GDataBatchStatus status = entry.BatchData.Status;
                    Console.WriteLine("{0} failed ({1})", batchId, status.Reason);
                }
            }

            Console.WriteLine(isSuccess ? "Batch operations successful." : "Batch operations failed");
        }

        public void UpadateData(List<CellAddress> cells)
        {
            Dictionary<String, CellEntry> cellEntries = GetCellEntryMap(this._GDService, this._CellFeed, cells);

            CellFeed batchRequest = new CellFeed(this._CellQuery.Uri, this._GDService);
            foreach (CellAddress cellAddr in cells)
            {
                CellEntry batchEntry = cellEntries[cellAddr.IdString];
                batchEntry.InputValue = cellAddr.Value.ToString();
                batchEntry.BatchData = new GDataBatchEntryData(cellAddr.IdString, GDataBatchOperationType.update);
                batchRequest.Entries.Add(batchEntry);
            }

            // Submit the update
            CellFeed batchResponse = (CellFeed)this._GDService.Batch(batchRequest, new Uri(this._CellFeed.Batch));

            // Check the results
            bool isSuccess = true;
            foreach (CellEntry entry in batchResponse.Entries)
            {
                string batchId = entry.BatchData.Id;
                if (entry.BatchData.Status.Code != 200)
                {
                    isSuccess = false;
                    GDataBatchStatus status = entry.BatchData.Status;
                    Console.WriteLine("{0} failed ({1})", batchId, status.Reason);
                }
            }

            Console.WriteLine(isSuccess ? "Batch operations successful." : "Batch operations failed");
        }

        public void SendByInterval()
        {
            List<CellAddress> cells = new List<CellAddress>();
            int totalRows = this.DatosEsts.Count;
            int totalLoaded = 0;
            IUploadLog _up = new Protell.Server.DAL.Repository.UploadLogRepository();
            long pastUnid = 0;
            long curUnid = 0;

            //Connect to Google
            curUnid = new Protell.Server.DAL.UNID().getNewUNID();
            curUnid = (pastUnid == curUnid ? curUnid + 1 : curUnid);
            pastUnid = curUnid;
            _up.InsertUploadLogServer(new Model.UploadLogModel()
            {
                IdUploadLog = curUnid,
                IdUsuario = 1,
                IpDir = "Unknown",
                PcName = "Unknown",
                Msg = "Send to Spreedshet - Connecting to Google"
            });
            this.Connecto2Google();

            //Load labels
            curUnid = new Protell.Server.DAL.UNID().getNewUNID();
            curUnid = (pastUnid == curUnid ? curUnid + 1 : curUnid);
            pastUnid = curUnid;
            _up.InsertUploadLogServer(new Model.UploadLogModel()
            {
                IdUploadLog = curUnid,
                IdUsuario = 1,
                IpDir = "Unknown",
                PcName = "Unknown",
                Msg = "Send to Spreedshet - Loading Labels"
            });
            cells = this.LoadLabelAddress();
            this.UpadateData(cells);

            //Load Data
            curUnid = new Protell.Server.DAL.UNID().getNewUNID();
            curUnid = (pastUnid == curUnid ? curUnid + 1 : curUnid);
            pastUnid = curUnid;
            _up.InsertUploadLogServer(new Model.UploadLogModel()
            {
                IdUploadLog = curUnid,
                IdUsuario = 1,
                IpDir = "Unknown",
                PcName = "Unknown",
                Msg = "Send to Spreedshet - Loading Data"
            });

            int i = 0;
            for (i = 0; i < totalRows; i++)
            {


                cells = this.LoadCellAddress(i, i + this.INTERVAL_NUM);
                this.UpadateData(cells);
                i = i + INTERVAL_NUM - 1;
                totalLoaded += INTERVAL_NUM;

                curUnid = new Protell.Server.DAL.UNID().getNewUNID();
                curUnid = (pastUnid == curUnid ? curUnid + 1 : curUnid);
                pastUnid = curUnid;


                try
                {
                    _up.InsertUploadLogServer(new Model.UploadLogModel()
                    {
                        IdUploadLog = curUnid,
                        IdUsuario = 1,
                        IpDir = "Unknown",
                        PcName = "Unknown",
                        Msg = "Send to Spreedshet - Data loaded from " + i.ToString()
                    });
                }
                catch (Exception)
                {
                    ;
                }
            }

            //Final Data
            if (totalLoaded < totalRows)
            {
                curUnid = new Protell.Server.DAL.UNID().getNewUNID();
                curUnid = (pastUnid == curUnid ? curUnid + 1 : curUnid);
                pastUnid = curUnid;
                _up.InsertUploadLogServer(new Model.UploadLogModel()
                {
                    IdUploadLog = curUnid,
                    IdUsuario = 1,
                    IpDir = "Unknown",
                    PcName = "Unknown",
                    Msg = "Send to Spreedshet - Loading Final Data"
                });

                cells = this.LoadCellAddress(totalLoaded, totalRows);
                this.UpadateData(cells);
            }

        }

        public void SendData()
        {
            this.Connecto2Google();
            this.LoadCellAddress();
            this.UpadateData();
        }

        private void Connecto2Google()
        {
            this._GDService = new SpreadsheetsService(this.Settings.GDataSpreadSheetName);
            this._GDService.setUserCredentials(this.Settings.GDataUserName, this.Settings.GDataUserPassword);
            this._CellQuery = new CellQuery(this.Settings.GDataSpreadsheetKey, "od6", "private", "full");
            this._CellFeed = this._GDService.Query(this._CellQuery);
        }

        private static Dictionary<String, CellEntry> GetCellEntryMap(
            SpreadsheetsService service, CellFeed cellFeed, List<CellAddress> cellAddrs)
        {
            CellFeed batchRequest = new CellFeed(new Uri(cellFeed.Self), service);
            foreach (CellAddress cellId in cellAddrs)
            {
                CellEntry batchEntry = new CellEntry(cellId.Row, cellId.Col, cellId.IdString);
                batchEntry.Id = new AtomId(string.Format("{0}/{1}", cellFeed.Self, cellId.IdString));
                batchEntry.BatchData = new GDataBatchEntryData(cellId.IdString, GDataBatchOperationType.query);
                batchRequest.Entries.Add(batchEntry);
            }

            CellFeed queryBatchResponse = (CellFeed)service.Batch(batchRequest, new Uri(cellFeed.Batch));

            Dictionary<String, CellEntry> cellEntryMap = new Dictionary<String, CellEntry>();
            foreach (CellEntry entry in queryBatchResponse.Entries)
            {
                cellEntryMap.Add(entry.BatchData.Id, entry);
                Console.WriteLine("batch {0} (CellEntry: id={1} editLink={2} inputValue={3})",
                    entry.BatchData.Id, entry.Id, entry.EditUri,
                    entry.InputValue);
            }

            return cellEntryMap;
        }

        //Constructor
        public Send2GDViewModel()
        {
            this._SettingRepository = new Protell.Server.DAL.Repository.SettingRepository();
            this._GDDatEstRepository = new Protell.Server.DAL.Repository.GdDatosEstructurasRepository();

            //Cargar datos de configuraciones
            this.Settings = this._SettingRepository.GetSetting();
            this.DatosEsts = (ObservableCollection<GdDatosEstructurasModel>)this._GDDatEstRepository.GetData();
        }
    }
}
